/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include "wifi_device.h"
#include "wifi_error_code.h"
#include "wifi/wifi_event.h"
#include "wifi/wifi_scan_info.h"
#include "wifi/wifi_linked_info.h"
#include "lwip/if_api.h"
#include "lwip/netifapi.h"
#include "lwip/dns.h"

#define CHANNEL_80211B_ONLY 14
#define FREQ_OF_CHANNEL_1 2412
#define FREQ_OF_CHANNEL_80211B_ONLY 2484
#define WIFI_MIN_CHANNEL 1
#define WIFI_FREQ_INTERVAL 5
#define WLAN_STA_NAME "wlan0"



typedef struct {
    WifiDeviceConfig config[WIFI_MAX_CONFIG_SIZE];
} DevWifiInfo_t;

static bool g_isRegisterWifiEvent = false;
static WifiEvent *g_event;
static wifi_event_stru g_event_handler;
static DevWifiInfo_t DevWifiInfo = {0};

static WifiScanType TransScanType(wifi_scan_type_enum type)
{
    switch (type) {
        case WIFI_SSID_SCAN:
            return HI3863_WIFI_SSID_SCAN;
        case WIFI_BSSID_SCAN:
            return HI3863_WIFI_BSSID_SCAN;
        case WIFI_FREQ_SCAN:
            return WIFI_CHANNEL_SCAN;
        default:
            return WIFI_BASIC_SCAN;
    }
}

static void OnWifiScanStateChangedCallback(int32_t state, int32_t size)
{
    (void)size;
    if (state == WIFI_STATE_AVALIABLE) {
        // g_isStaScanSuccess = true;
    }
    if (g_event->OnWifiScanStateChanged) {
        g_event->OnWifiScanStateChanged(state, size);
    }
}

static void OnWifiConnectionChangedCallback(int32_t state, const wifi_linked_info_stru *info, int32_t reason_code)
{
    if (info == NULL) {
        printf("param invalid\r\n");
        return;
    }
    (void)state;
    WifiLinkedInfo ohos_info;
    memcpy_s(ohos_info.ssid, WIFI_MAX_SSID_LEN, info->ssid, WIFI_MAX_SSID_LEN);
    memcpy_s(ohos_info.bssid, WIFI_MAC_LEN, info->bssid, WIFI_MAC_LEN);
    ohos_info.rssi = info->rssi;
    ohos_info.frequency = ChannelToFrequency(info->channel_num);

    if (g_event->OnWifiConnectionChanged) {
        g_event->OnWifiConnectionChanged(state, &ohos_info);
    }
    // printf("state: %d, disconnectedReason %u\r\n", state, info->conn_state);
}

static void OnHotspotStaJoinCallback(const wifi_sta_info_stru *info)
{
    if (info == NULL) {
        printf("param invalid\r\n");
        return;
    }
    (void)info;
    StationInfo ohos_info;
    memcpy_s(ohos_info.macAddress, WIFI_MAC_LEN, info->mac_addr, WIFI_MAC_LEN);
    if (g_event->OnHotspotStaJoin) {
        g_event->OnHotspotStaJoin(&ohos_info);
    }
}

static void OnHotspotStaLeaveCallback(const wifi_sta_info_stru *info)
{
        if (info == NULL) {
        printf("param invalid\r\n");
        return;
    }
    (void)info;
    StationInfo ohos_info;
    memcpy_s(ohos_info.macAddress, WIFI_MAC_LEN, info->mac_addr, WIFI_MAC_LEN);
    if (g_event->OnHotspotStaLeave) {
        g_event->OnHotspotStaLeave(&ohos_info);
    }
}

static void OnHotspotStateChangedCallback(int state)
{
    (void)state;
    if (g_event->OnHotspotStateChanged) {
        g_event->OnHotspotStateChanged(state);
    }
}

WifiErrorCode EnableWifi(void)
{
    LockWifiGlobalLock();
    if (wifi_is_sta_enabled() == WIFI_STA_ACTIVE) {
        UnlockWifiGlobalLock();
        return ERROR_WIFI_BUSY;
    }
    int ret = wifi_sta_enable();
    if (ret != 0) {
        printf("[wifi_service]:EnableWifi fail, err = %d\n", ret);
        UnlockWifiGlobalLock();
        return ERROR_WIFI_UNKNOWN;
    }
    UnlockWifiGlobalLock();
    return WIFI_SUCCESS;
}


WifiErrorCode DisableWifi(void)
{
    LockWifiGlobalLock();
    if (wifi_is_sta_enabled() == WIFI_STA_NOT_ACTIVE) {
        UnlockWifiGlobalLock();
        return ERROR_WIFI_NOT_STARTED;
    }
    int ret = wifi_sta_disable();
    if (ret != 0) {
        printf("[wifi_service]:DisableWifi fail, err = %d\n", ret);
        UnlockWifiGlobalLock();
        return ERROR_WIFI_UNKNOWN;
    }
    UnlockWifiGlobalLock();
    return WIFI_SUCCESS;
}


int IsWifiActive(void)
{
    return wifi_is_sta_enabled();
}


WifiErrorCode Scan(void)
{
    int ret = wifi_sta_scan();
    if (ret != 0) {
        printf("[wifi_service]:Scan fail, err = %d\n", ret);
        return ERROR_WIFI_UNKNOWN;
    }
    return WIFI_SUCCESS;
}


WifiErrorCode GetScanInfoList(WifiScanInfo *result, unsigned int *size)
{
    wifi_scan_info_stru ScanList[WIFI_SCAN_HOTSPOT_LIMIT] = {0};
    int ret = wifi_sta_get_scan_info(ScanList, size);
    if (ret != 0) {
        printf("[wifi_service]:GetScanInfoList fail, err = %d\n", ret);
        return ERROR_WIFI_UNKNOWN;
    }
    for(int i =0; i < *size; i++) {
        memcpy_s(result[i].ssid, WIFI_MAX_SSID_LEN, ScanList[i].ssid, WIFI_MAX_SSID_LEN);
        memcpy_s(result[i].bssid, WIFI_MAC_LEN, ScanList[i].bssid, WIFI_MAC_LEN);
        result[i].rssi = ScanList[i].rssi;
        result[i].band = ScanList[i].band;
        result[i].securityType = HiSecToHoSec(ScanList[i].security_type);
        result[i].frequency = ChannelToFrequency(ScanList[i].channel_num);
    }
    return WIFI_SUCCESS;
}


WifiErrorCode AddDeviceConfig(const WifiDeviceConfig *config, int *result)
{
    DevWifiInfo_t *info = &DevWifiInfo;
    if (!config)
        return ERROR_WIFI_INVALID_ARGS;
    for (unsigned i = 0; i < WIFI_MAX_CONFIG_SIZE; i++) {
        if (memcmp(config->ssid, &info->config[i].ssid, WIFI_MAX_SSID_LEN) == 0 &&
            memcmp(config->bssid,&info->config[i].bssid, WIFI_MAC_LEN) == 0 &&
            memcmp(config->preSharedKey, &info->config[i].preSharedKey, WIFI_MAX_KEY_LEN) == 0 &&
            config->securityType == info->config[i].securityType) {
            printf("repetitive configuration\r\n");
            if (result) {
                *result = i;
            }
            return WIFI_SUCCESS;
        }
    }
    LockWifiGlobalLock();
    for (unsigned i = 0; i < WIFI_MAX_CONFIG_SIZE; i++) {
        if (info->config[i].netId != i) {
            memcpy_s(&info->config[i], sizeof(WifiDeviceConfig), config, sizeof(WifiDeviceConfig));
            info->config[i].netId = i;
            if (result) {
                *result = i;
            }
            UnlockWifiGlobalLock();
            return WIFI_SUCCESS;
        }
    }
    if (result) {
        *result = 0;
    }
    UnlockWifiGlobalLock();
    return ERROR_WIFI_BUSY;
}


WifiErrorCode GetDeviceConfigs(WifiDeviceConfig *result, unsigned int *size)
{
    unsigned retIndex = 0, maxIndex;
    DevWifiInfo_t *info = &DevWifiInfo;
    if ((!result) || (!size))
        return ERROR_WIFI_INVALID_ARGS;
    if (!*size)
        return ERROR_WIFI_INVALID_ARGS;
    maxIndex = *size;
    LockWifiGlobalLock();
    for (unsigned i = 0; i < WIFI_MAX_CONFIG_SIZE; i++) {
        if (info->config[i].netId != i) {
            continue;
        }
        memcpy_s(&result[retIndex++], sizeof(WifiDeviceConfig), &info->config[i], sizeof(WifiDeviceConfig));
        if (maxIndex < retIndex) {
            break;
        }
    }
    UnlockWifiGlobalLock();
    if (retIndex == 0) {
        return ERROR_WIFI_NOT_AVAILABLE;
    }
    *size = retIndex;
    return WIFI_SUCCESS;
}


WifiErrorCode RemoveDevice(int networkId)
{
    DevWifiInfo_t *info = &DevWifiInfo;
    if ((networkId >= WIFI_MAX_CONFIG_SIZE) || (networkId < 0)) {
        return ERROR_WIFI_INVALID_ARGS;
    }
    LockWifiGlobalLock();
    memcpy_s(&info->config[networkId], sizeof(WifiDeviceConfig), 0, sizeof(WifiDeviceConfig));
    info->config[networkId].netId = WIFI_CONFIG_INVALID;
    UnlockWifiGlobalLock();
    return WIFI_SUCCESS;
}


// WifiErrorCode DisableDeviceConfig(int networkId)
// {

// }


// WifiErrorCode EnableDeviceConfig(int networkId)
// {

// }


WifiErrorCode ConnectTo(int networkId)
{
    WifiDeviceConfig *pconfig;
    DevWifiInfo_t *info = &DevWifiInfo;
    char ifname[WIFI_IFNAME_MAX_SIZE + 1] = "wlan0";
    struct netif *netif_p = NULL;
    if ((networkId >= WIFI_MAX_CONFIG_SIZE) || (networkId < 0)) {
        return ERROR_WIFI_INVALID_ARGS;
    }
    LockWifiGlobalLock();
    pconfig = &info->config[networkId];
    if (pconfig->netId != networkId) {
        UnlockWifiGlobalLock();
        return ERROR_WIFI_NOT_AVAILABLE;
    }
    wifi_sta_config_stru StaConfig = {0};
    memcpy_s(StaConfig.ssid, sizeof(StaConfig.ssid), pconfig->ssid, sizeof(pconfig->ssid));
    memcpy_s(StaConfig.bssid, sizeof(StaConfig.bssid), pconfig->bssid, sizeof(pconfig->bssid));
    memcpy_s(StaConfig.pre_shared_key, sizeof(StaConfig.pre_shared_key), pconfig->preSharedKey, sizeof(pconfig->preSharedKey));
    StaConfig.security_type = HoSecToHiSec(pconfig->securityType);
    StaConfig.wifi_psk_type = pconfig->wapiPskType;
    StaConfig.ip_type = pconfig->ipType;
    memcpy_s(StaConfig.static_ip.ip_address,sizeof(StaConfig.static_ip.ip_address), pconfig->staticIp.ipAddress, sizeof(pconfig->staticIp.ipAddress));
    memcpy_s(StaConfig.static_ip.gateway,sizeof(StaConfig.static_ip.gateway), pconfig->staticIp.gateway, sizeof(pconfig->staticIp.gateway));
    memcpy_s(StaConfig.static_ip.dns_servers,sizeof(StaConfig.static_ip.dns_servers), pconfig->staticIp.dnsServers, sizeof(pconfig->staticIp.dnsServers));
    memcpy_s(StaConfig.static_ip.netmask,sizeof(StaConfig.static_ip.netmask), pconfig->staticIp.netmask, sizeof(pconfig->staticIp.netmask));
    int ret = wifi_sta_connect(&StaConfig);
    if (ret != 0) {
        printf("[wifi_service]:Connect fail, err = %d\n", ret);
        UnlockWifiGlobalLock();
        return ERROR_WIFI_UNKNOWN;
    }
    UnlockWifiGlobalLock();
    wifi_linked_info_stru wifi_status;
    for (int i = 0; i < 5; i ++) {
        osDelay(50);
        memset_s(&wifi_status, sizeof(wifi_linked_info_stru), 0, sizeof(wifi_linked_info_stru));
        if (wifi_sta_get_ap_info(&wifi_status) != 0) {
            continue;
        }
        if (wifi_status.conn_state == 1) {
            break;
        }
    }
    if (wifi_status.conn_state != 1) {
        return ERROR_WIFI_UNKNOWN;
    }
    netif_p = netifapi_netif_find(ifname);
    if (netif_p == NULL) {
        return ERROR_WIFI_UNKNOWN;
    }
    if (netifapi_dhcp_start(netif_p) != 0) {
        printf("STA DHCP Fail.\r\n");
        return ERROR_WIFI_UNKNOWN;
    }
    return WIFI_SUCCESS;
}


WifiErrorCode ConnectToDevice(const WifiDeviceConfig *config)
{

}


WifiErrorCode Disconnect(void)
{
    int ret = wifi_sta_disconnect();
    if (ret != 0) {
        printf("[wifi_service]:Disconnect fail, err = %d\n", ret);
        return ERROR_WIFI_UNKNOWN;
    }
    return WIFI_SUCCESS;
}

static int GetLocalWifiIp(int * const ip)
{
    int ret;
    struct netif *netif_node = netif_find(WLAN_STA_NAME);
    if (netif_node == NULL) {
        printf("GetLocalWifiIp netif get fail\r\n");
        return ERROR_WIFI_INVALID_ARGS;
    }

    ip4_addr_t ipAddr;
    ip4_addr_t netMask;
    ip4_addr_t gateWay;

    ret = netifapi_netif_get_addr(netif_node, &ipAddr, &netMask, &gateWay);
    if (ret == 0) {
        *ip = ip4_addr_get_u32(&ipAddr);
        return WIFI_SUCCESS;
    }
    return ret;
}

WifiErrorCode GetLinkedInfo(WifiLinkedInfo *result)
{
    wifi_linked_info_stru LinkedInfo = {0};
    int ret = wifi_sta_get_ap_info(&LinkedInfo);
    if (ret != 0) {
        printf("[GetLinkedInfo]:wifi_sta_get_ap_info fail, err = %d\n", ret);
        return ERROR_WIFI_UNKNOWN;
    }
    memcpy_s(result->ssid, WIFI_MAX_SSID_LEN, LinkedInfo.ssid, WIFI_MAX_SSID_LEN);
    memcpy_s(result->bssid, WIFI_MAC_LEN, LinkedInfo.bssid, WIFI_MAC_LEN);
    result->rssi = LinkedInfo.rssi;
    result->connState = LinkedInfo.conn_state;
    if (GetLocalWifiIp(&(result->ipAddress)) != EOK) {
        printf("[GetLinkedInfo]:GetLocalWifiIp fail, err = %d\n", ret);
        return ERROR_WIFI_UNKNOWN;
    }
    return WIFI_SUCCESS;
}


WifiErrorCode GetDeviceMacAddress(unsigned char *result)
{
    wifi_linked_info_stru LinkedInfo = {0};
    wifi_sta_get_ap_info(&LinkedInfo);
    memcpy_s(result, WIFI_MAC_LEN, LinkedInfo.bssid, WIFI_MAC_LEN);
    return WIFI_SUCCESS;
}


WifiErrorCode AdvanceScan(WifiScanParams *params)
{
    if (params == NULL) {
        return ERROR_WIFI_UNKNOWN;
    }

    if (wifi_is_sta_enabled() == WIFI_STA_NOT_ACTIVE) {
        return ERROR_WIFI_NOT_STARTED;
    }
    wifi_scan_params_stru *ScanParams = {0};
    memcpy_s(ScanParams->ssid, WIFI_MAX_SSID_LEN, params->ssid, WIFI_MAX_SSID_LEN);
    memcpy_s(ScanParams->ssid_len, WIFI_MAX_SSID_LEN, params->ssidLen, WIFI_MAX_SSID_LEN);
    memcpy_s(ScanParams->bssid, WIFI_MAC_LEN, params->bssid, WIFI_MAC_LEN);
    ScanParams->channel_num = FrequencyToChannel (params->freqs);
    ScanParams->scan_type = TransScanType (params->scanType);
    int ret = wifi_sta_scan_advance(ScanParams);
    if (ret != 0) {
        printf("[wifi_service]:GetLinkedInfo fail, err = %d\n", ret);
        return ERROR_WIFI_UNKNOWN;
    }
    return WIFI_SUCCESS;
}


WifiErrorCode GetIpInfo(IpInfo *info)
{
    int ret;
    struct netif *netif_node = netif_find(WLAN_STA_NAME);
    if (netif_node == NULL) {
        printf("GetLocalWifiIp netif get fail\r\n");
        return ERROR_WIFI_INVALID_ARGS;
    }

    ip4_addr_t ipAddr;
    ip4_addr_t netMask;
    ip4_addr_t gateWay;

    ret = netifapi_netif_get_addr(netif_node, &ipAddr, &netMask, &gateWay);
    if (ret == 0) {
        info->ipAddress = ip4_addr_get_u32(&ipAddr);
        info->netMask = ip4_addr_get_u32(&netMask);
        info->netGate = ip4_addr_get_u32(&gateWay);
        // printf("&&&&&&&IP address%s\r\n",info->ipAddress);
        return WIFI_SUCCESS;
    }
    return ERROR_WIFI_UNKNOWN;
}

WifiErrorCode RegisterWifiEvent(WifiEvent *event)
{
    if (g_isRegisterWifiEvent) {
        return ERROR_WIFI_BUSY;
    }
    if (event == NULL) {
        return ERROR_WIFI_INVALID_ARGS;
    }
    LockWifiEventLock();
    g_event = event;
    g_event_handler.wifi_event_scan_state_changed = OnWifiScanStateChangedCallback;
    g_event_handler.wifi_event_connection_changed = OnWifiConnectionChangedCallback;
    g_event_handler.wifi_event_softap_sta_join = OnHotspotStaJoinCallback;
    g_event_handler.wifi_event_softap_sta_leave = OnHotspotStaLeaveCallback;
    g_event_handler.wifi_event_softap_state_changed = OnHotspotStateChangedCallback;

    int ret = wifi_register_event_cb(&g_event_handler);
    if (ret != 0) {
        printf("[wifi_service]:RegisterWifiEvent fail, err = %d\n", ret);
        UnlockWifiEventLock();
        return ERROR_WIFI_UNKNOWN;
    }
    UnlockWifiEventLock();
    g_isRegisterWifiEvent = true;
    return WIFI_SUCCESS;
}


WifiErrorCode UnRegisterWifiEvent(const WifiEvent *event)
{
    if (!g_isRegisterWifiEvent) {
        return ERROR_WIFI_BUSY;
    }
    LockWifiEventLock();
    if (event != g_event) {
        UnlockWifiEventLock();
        return ERROR_WIFI_UNKNOWN;
    }
    int ret = wifi_unregister_event_cb(&g_event_handler);
    if (ret != 0) {
        printf("[wifi_service]:RegisterWifiEvent fail, err = %d\n", ret);
        UnlockWifiEventLock();
        return ERROR_WIFI_UNKNOWN;
    }
    memset_s(&g_event_handler,sizeof(wifi_event_stru),0,sizeof(wifi_event_stru));
    memset_s(g_event,sizeof(WifiEvent),0,sizeof(WifiEvent));
    g_isRegisterWifiEvent = false;
    UnlockWifiEventLock();
    return WIFI_SUCCESS;
}

